﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace SWMapEditor
{
    /// <summary>
    /// Displays a tile grid consisting of 3 layers of images
    /// </summary>
    public partial class MapGrid : UserControl
    {
        public enum MapGridLayer
        {
            Terrain, ForegroundOne, ForegroundTwo
        }


        #region Properties
        /// <summary>
        /// Where to load map times from
        /// </summary>
        string m_mapImageDirectory = @"C:\games\Shadow Worlds\tga\mapimg";
        public string MapImageDirectory
        {
            get { return m_mapImageDirectory; }
            set
            {
                m_mapImageDirectory = value;
                //Reload map images if this changes
                LoadMapImages();
            }
        }

        /// <summary>
        /// The width of the viewport in tiles
        /// </summary>
        public int ViewportTileWidth
        {
            get { return (int)Math.Ceiling((double)Width / 10); }
        }

        /// <summary>
        /// The height of the viewport in tiles
        /// </summary>
        public int ViewportTileHeight
        {
            get { return (int)Math.Ceiling((double)Height / 10); }
        }


        /// <summary>
        /// The current X Offset of the tile grid
        /// </summary>
        int m_xOffset;
        public int XOffset
        {
            get { return m_xOffset; }
            set
            {
                m_xOffset = value;

                //Clamp X offset so that you can't scroll past
                //the edges of the grid
                if (m_xOffset >= TileWidth - ViewportTileWidth)
                {
                    m_xOffset = TileWidth - ViewportTileWidth;
                }

                if (m_xOffset < 0)
                {
                    m_xOffset = 0;
                }

                TryInvalidate();
            }
        }

        /// <summary>
        /// The current Y Offset of the tile grid
        /// </summary>
        int m_yOffset;
        public int YOffset
        {
            get { return m_yOffset; }
            set
            {
                m_yOffset = value;

                //Clamp the Y offset so that you can't scroll past
                //the edges of the grid
                if (m_yOffset >= TileHeight - ViewportTileHeight)
                {
                    m_yOffset = TileHeight - ViewportTileHeight;
                }

                if (m_yOffset < 0)
                {
                    m_yOffset = 0;
                }

                TryInvalidate();
            }
        }

        /// <summary>
        /// The width of the grid in tiles
        /// </summary>
        int m_tileWidth = 50;
        public int TileWidth
        {
            get { return m_tileWidth; }
            set
            {
                m_tileWidth = value;
                TryInvalidate();
            }
        }

        /// <summary>
        /// The height of the grid in tiles
        /// </summary>
        int m_tileHeight = 50;
        public int TileHeight 
        {
            get { return m_tileHeight; }
            set 
            { 
                m_tileHeight = value;
                TryInvalidate();
            }
        }
        #endregion

        //blocks repainting when true
        bool m_invalidateLock = false;

        //Number grids that correspond to tile numbers for each square on the grid
        int[,] m_terrainLayer;
        int[,] m_foregroundLayerOne;
        int[,] m_foregroundLayerTwo;
        
        //A list of tile images
        Dictionary<int, Image> m_images;

        public MapGrid()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Initialises the tile grid
        /// </summary>
        void InitialiseGrid()
        {
            //Initialise the number grids
            m_terrainLayer = new int[TileWidth, TileHeight];
            m_foregroundLayerOne = new int[TileWidth, TileHeight];
            m_foregroundLayerTwo = new int[TileWidth, TileHeight];

            for (int x = 0; x < TileWidth; x++)
            {
                for (int y = 0; y < TileHeight; y++)
                {
                    //Initialise each tile value to -1 (no tile)
                    m_terrainLayer[x, y] = -1;
                    m_foregroundLayerOne[x, y] = -1;
                    m_foregroundLayerTwo[x, y] = -1;
                }
            }

            //Repaint
            TryInvalidate();
        }

        /// <summary>
        /// Loads all map tile images from the MapImageDirectory
        /// </summary>
        void LoadMapImages()
        {
            m_images = new Dictionary<int, Image>();

            string[] imageFiles = null;
            try
            {
                //Attempt to get a list of images from the specified directory.
                //Match only files that end with .BMP000.png
                imageFiles = Directory.GetFiles(MapImageDirectory, "*.BMP000.png");
            }
            catch
            {
                //If the loading failed, then bail out
                MessageBox.Show("Failed to load map images. Did you specify a Map Image Directory?");
                return;
            }

            //Load each image from the images directory
            foreach (string f in imageFiles)
            {
                try
                {
                    Uri imageURI = new Uri(f);
                    //Get the image number for the tile by breaking the number out from the file name
                    int imageNumber = int.Parse(imageURI.Segments[imageURI.Segments.Length - 1].Split('.')[0]);

                    //Load the image from file and add it to the list
                    m_images.Add(imageNumber, Image.FromFile(f));
                }
                catch
                {
                    //Skip the image if we failed to load it / get its image number
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (!m_invalidateLock)
            {
                //Draw each layer in order if repainting isn't locked
                DrawLayer(e.Graphics, m_terrainLayer);
                DrawLayer(e.Graphics, m_foregroundLayerOne);
                DrawLayer(e.Graphics, m_foregroundLayerTwo);
            }
        }

        void DrawLayer(Graphics g, int[,] layer)
        {     
            //For each tile in the grid, draw the image if necessary.
            for (int x = 0; x < ViewportTileWidth + 13; x++)
            {
                //Use a 13 tile padding so that tiles on the left and top edges don't disappear when the offset changes
                int tileX = x + XOffset - 13;

                //If the chosen tile is out of bounds, then skip onwards
                if (tileX < 0 || tileX >= TileWidth)
                {
                    continue;
                }

                for (int y = 0; y < ViewportTileHeight + 13; y++)
                {
                    int tileY = y + YOffset - 13;

                    if (tileY < 0 || tileY >= TileHeight)
                    {
                        continue;
                    }

                    //If the tile is less than 0, skip onwards
                    if (layer[tileX, tileY] < 0)
                    {
                        continue;
                    }

                    //Try to get the image for this tile number
                    Image tile = null;
                    m_images.TryGetValue(layer[tileX, tileY], out tile);

                    //If an image was found, draw it!
                    if (tile != null)
                    {
                        g.DrawImage(tile, (x * 10) - 130, (y * 10) - 130, tile.Width, tile.Height);
                    }
                }
            }
        }


        /// <summary>
        /// Repaint if repainting is not locked.
        /// </summary>
        void TryInvalidate()
        {
            if (!m_invalidateLock)
            {
                Invalidate();
            }
        }

        /// <summary>
        /// Block repainting from occurring.
        /// </summary>
        void LockInvalidate()
        {
            m_invalidateLock = true;
        }

        /// <summary>
        /// Allow repainting to occur and force a repaint.
        /// </summary>
        void UnlockInvalidate()
        {
            m_invalidateLock = false;
            Invalidate();
        }

        private void MapGrid_Load(object sender, EventArgs e)
        {
            //Paint to memory before blitting to the form surface
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);

            //Load images and initialise
            LoadMapImages();
            InitialiseGrid();
        }
    }
}
